/*
 * Copyright (c) gitee.com/jiandong001.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { ViewAtIndex } from './ViewAtIndex';
import { PixelUtil } from './PixelUtil';
import { ReadableArray } from '../bridge/ReadableArray';
import { Assertions } from '../inner/Assertions';
import { RootViewManager } from './RootViewManager';
import { NativeViewHierarchyManager } from './NativeViewHierarchyManager';
import { ReactBuildConfig } from '../common/build/ReactBuildConfig';
import { UIBlock } from './UIBlock';
import { ReadableMap } from '../bridge/ReadableMap';
import { ThemedReactContext } from './ThemedReactContext';
import { View } from './View';
import { Runnable } from '../bridge/queue/MessageQueueThread';
import hilog from '@ohos.hilog';
import { ViewManager } from './ViewManager';
import { ReactShadowNode } from './ReactShadowNode';
import { ReactShadowNodeImpl } from './ReactShadowNodeImpl';
import { EventDispatcher } from './events/EventDispatcher';
import { UIViewOperationQueue } from './UIViewOperationQueue';
import { ViewManagerRegistry, ReactShadowNodeViewManager } from './ViewManagerRegistry';
import { ShadowNodeRegistry } from './ShadowNodeRegistry';
import { ReactStylesDiffMap } from './ReactStylesDiffMap';
import { ReactContext } from '../bridge/ReactContext';
import { NativeViewHierarchyOptimizer } from './NativeViewHierarchyOptimizer'
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

export interface LayoutUpdateListener {
  onLayoutUpdated(var1: ReactShadowNode): void;
}

export class UIImplementation {
  private static readonly TAG = 'react UIImplementation'
  //    protected Object uiImplementationThreadLock;
  protected mEventDispatcher: EventDispatcher;
  protected mReactContext: ReactContext;
  protected mShadowNodeRegistry: ShadowNodeRegistry;
  protected mLayoutUpdateListener?: LayoutUpdateListener;
  private mViewManagers: ViewManagerRegistry;
  private mOperationsQueue: UIViewOperationQueue;
  private mNativeViewHierarchyOptimizer: NativeViewHierarchyOptimizer;
  private mMeasureBuffer: number[];
  private mLastCalculateLayoutTime: number; //long
  private mViewOperationsEnabled: boolean;

  //    public UIImplementation(ReactApplicationContext reactContext, ViewManagerResolver viewManagerResolver, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) {
  //        this(reactContext, new ViewManagerRegistry(viewManagerResolver), eventDispatcher, minTimeLeftInFrameForNonBatchedOperationMs);
  //    }
  //
  //    public UIImplementation(ReactApplicationContext reactContext, List<ViewManager> viewManagers, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) {
  //        this(reactContext, new ViewManagerRegistry(viewManagers), eventDispatcher, minTimeLeftInFrameForNonBatchedOperationMs);
  //    }
  //
  //    UIImplementation(ReactApplicationContext reactContext, ViewManagerRegistry viewManagers, EventDispatcher eventDispatcher, int minTimeLeftInFrameForNonBatchedOperationMs) {
  //        this(reactContext, viewManagers, new UIViewOperationQueue(reactContext, new NativeViewHierarchyManager(viewManagers), minTimeLeftInFrameForNonBatchedOperationMs), eventDispatcher);
  //    }
  //    public constructor(reactContext: ReactContext, minTimeLeftInFrameForNonBatchedOperationMs:number) {
  public constructor(reactContext: ReactContext, viewManagers: ViewManagerRegistry, eventDispatcher: EventDispatcher, minTimeLeftInFrameForNonBatchedOperationMs: number) {
    hilog.info(0x0000, UIImplementation.TAG, 'constructor')

    //        this.uiImplementationThreadLock = new Object();
    this.mShadowNodeRegistry = new ShadowNodeRegistry();
    this.mMeasureBuffer = new Array<number>(4);
    this.mLastCalculateLayoutTime = 0;
    this.mViewOperationsEnabled = true;
    this.mReactContext = reactContext;
    this.mViewManagers = viewManagers;
    this.mOperationsQueue = new UIViewOperationQueue(reactContext, new NativeViewHierarchyManager(viewManagers, new RootViewManager()), minTimeLeftInFrameForNonBatchedOperationMs);
    this.mNativeViewHierarchyOptimizer = new NativeViewHierarchyOptimizer(this.mOperationsQueue, this.mShadowNodeRegistry);
    this.mEventDispatcher = eventDispatcher;
    hilog.info(0x0000, UIImplementation.TAG, 'constructor3333333333333')
  }

  public resolveShadowNode(reactTag: number): ReactShadowNode {
    hilog.info(0x0000, UIImplementation.TAG, 'resolveShadowNode')
    return this.mShadowNodeRegistry.getNode(reactTag);
  }

  public getUIViewOperationQueue(): UIViewOperationQueue {
    return this.mOperationsQueue;
  }

  public updateRootView(tag: number, widthMeasureSpec: number, heightMeasureSpec: number): void {
    hilog.info(0x0000, UIImplementation.TAG, 'updateRootView')
    let rootCSSNode: ReactShadowNode = this.mShadowNodeRegistry.getNode(tag);
    if (rootCSSNode == null) {
      hilog.info(0x0000, UIImplementation.TAG, '%{public}s', "Tried to update non-existent root tag: " + tag);
    } else {
      this.updateRootViewImpl(rootCSSNode, widthMeasureSpec, heightMeasureSpec);
    }
  }

  public updateRootViewImpl(rootCSSNode: ReactShadowNode, widthMeasureSpec: number, heightMeasureSpec: number): void {
    hilog.info(0x0000, UIImplementation.TAG, 'updateRootViewImpl')
    // rootCSSNode.setMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
  }

  public registerRootView<T extends View>(rootView: T, tag: number, context: ThemedReactContext) {
    hilog.info(0x0000, UIImplementation.TAG, 'registerRootView')
    //        synchronized(this.uiImplementationThreadLock) {
    let rootCSSNode: ReactShadowNode = this.createRootShadowNode();
    rootCSSNode.setReactTag(tag);
    rootCSSNode.setThemedContext(context);
    //            context.runOnNativeModulesQueueThread(new Runnable() {
    //                public void run() {
    this.mShadowNodeRegistry.addRootNode(rootCSSNode);
    //                }
    //            });
    this.mOperationsQueue.addRootView(tag, rootView);
    //        }
  }

  public removeRootView(rootViewTag: number) {
    hilog.info(0x0000, UIImplementation.TAG, 'removeRootView')
    this.removeRootShadowNode(rootViewTag);
    this.mOperationsQueue.enqueueRemoveRootView(rootViewTag);
  }

  public removeRootShadowNode(rootViewTag: number) {
    //        synchronized(this.uiImplementationThreadLock) {
    this.mShadowNodeRegistry.removeRootNode(rootViewTag);
    //        }
  }

  public updateNodeSize(nodeViewTag: number, newWidth: number, newHeight: number): void {
    let cssNode: ReactShadowNode = this.mShadowNodeRegistry.getNode(nodeViewTag);
    if (cssNode == null) {
      hilog.debug(0x0000, UIImplementation.TAG, '%{public}s', "Tried to update size of non-existent tag: " + nodeViewTag);
    } else {
      cssNode.setStyleWidth(newWidth);
      cssNode.setStyleHeight(newHeight);
      this.dispatchViewUpdatesIfNeeded();
    }
  }

  public createView(tag: number, typeName: string, rootViewTag: number, props: ReadableMap): void {
    hilog.info(0x0000, UIImplementation.TAG, 'createView %{public}d', 111)
    if (!this.mViewOperationsEnabled) {
      return
    }
    ;

    let cssNode: ReactShadowNode = this.createShadowNode(typeName);
    let rootNode: ReactShadowNode = this.mShadowNodeRegistry.getNode(rootViewTag);
    Assertions.assertNotNull(rootNode != null, "Root node with tag " + rootViewTag + " doesn't exist");
    cssNode.setReactTag(tag);
    cssNode.setViewTypeName(typeName);
    cssNode.setRootTag(rootNode.getReactTag());
    // cssNode.setThemedContext(rootNode.getThemedContext());
    this.mShadowNodeRegistry.addNode(cssNode);
    let styles: ReactStylesDiffMap = null;
    //                if (props != null) {
    //                    styles = new ReactStylesDiffMap(props);
    //                    cssNode.updateProperties(styles);
    //                }
    this.handleCreateView(cssNode, rootViewTag, styles);
    //
  }

  public updateView(tag: number, className: string, props: ReadableMap) {
    if (!this.mViewOperationsEnabled) {
      return
    }
    let viewManager: ReactShadowNodeViewManager = this.mViewManagers.get(className);
    if (viewManager == null) {
      throw new Error("Got unknown view type: " + className);
    } else {
      let cssNode: ReactShadowNode = this.mShadowNodeRegistry.getNode(tag);
      if (cssNode == null) {
        throw new Error("Trying to update non-existent view with tag " + tag);
      } else {
        if (props != null) {
          let styles: ReactStylesDiffMap = new ReactStylesDiffMap(props);
          cssNode.updateProperties(styles);
          this.handleUpdateView(cssNode, className, styles);
        }

      }
    }
  }

  public synchronouslyUpdateViewOnUIThread(tag: number, props: ReactStylesDiffMap): void {
    //        UiThreadUtil.assertOnUiThread();
    this.mOperationsQueue.getNativeViewHierarchyManager().updateProperties(tag, props);
  }

  //    public setViewLocalData(tag: number, data: any) {
  //        shadowNode: ReactShadowNode = this.mShadowNodeRegistry.getNode(tag);
  //        if (shadowNode == null) {
  //            hilog.info(0x0000, "ReactNative", "Attempt to set local data for view with unknown tag: " + tag);
  //        } else {
  //            shadowNode.setLocalData(data);
  //            this.dispatchViewUpdatesIfNeeded();
  //        }
  //    }
  //
  //    public void profileNextBatch() {
  //        this.mOperationsQueue.profileNextBatch();
  //    }
  //
  //    public Map<String, Long> getProfiledBatchPerfCounters() {
  //        return this.mOperationsQueue.getProfiledBatchPerfCounters();
  //    }

  public handleUpdateView(cssNode: ReactShadowNode, className: string, styles: ReactStylesDiffMap): void {
    if (!cssNode.isVirtual()) {
      this.mNativeViewHierarchyOptimizer.handleUpdateView(cssNode, className, styles);
    }
  }

  // ========================1212===========
  public dispatchViewUpdates(batchId: number) {
    //  SystraceMessage.beginSection(0L, "UIImplementation.dispatchViewUpdates").arg("batchId", batchId).flush();
    let commitStartTime: number = 0; // SystemClock.uptimeMillis();

    try {
      this.updateViewHierarchy();
      this.mNativeViewHierarchyOptimizer.onBatchComplete();
      this.mOperationsQueue.dispatchViewUpdates(batchId, commitStartTime, this.mLastCalculateLayoutTime);
    } finally {
      // Systrace.endSection(0L);
    }

  }

  public dismissPopupMenu() {
    //this.mOperationsQueue.enqueueDismissPopupMenu();
  }

  public sendAccessibilityEvent(tag: number, eventType: number) {
    //this.mOperationsQueue.enqueueSendAccessibilityEvent(tag, eventType);
  }

  public onHostResume() {
    //this.mOperationsQueue.resumeFrameCallback();
  }

  public manageChildren(viewTag: number, moveFrom?: ReadableArray, moveTo?: ReadableArray, addChildTags?: ReadableArray, addAtIndices?: ReadableArray, removeFrom?: ReadableArray) {
    if (!this.mViewOperationsEnabled) {
      return
    }

    //              synchronized(this.uiImplementationThreadLock) {
    let cssNodeToManage = this.mShadowNodeRegistry.getNode(viewTag);

    let numToMove = moveFrom == null ? 0 : moveFrom.size();
    let numToAdd = addChildTags == null ? 0 : addChildTags.size();
    let numToRemove = removeFrom == null ? 0 : removeFrom.size();
    if (numToMove == 0 || moveTo != null && numToMove == moveTo.size()) {
      if (numToAdd == 0 || addAtIndices != null && numToAdd == addAtIndices.size()) {
        let viewsToAdd = new Array<ViewAtIndex>(numToMove + numToAdd);
        let indicesToRemove = new Array<number>(numToMove + numToRemove);
        let tagsToRemove = new Array<number>(indicesToRemove.length);
        let tagsToDelete = new Array<number>(numToRemove);
        let lastIndexRemoved: number;
        let i: number;
        let indexToRemove: number;
        if (numToMove > 0) {
          Assertions.assertNotNull(moveFrom != null);
          Assertions.assertNotNull(moveTo != null);

          for (lastIndexRemoved = 0; lastIndexRemoved < numToMove; ++lastIndexRemoved) {
            i = moveFrom.getInt(lastIndexRemoved);
            indexToRemove = cssNodeToManage.getChildAt(i).getReactTag();
            viewsToAdd[lastIndexRemoved] = new ViewAtIndex(indexToRemove, moveTo.getInt(lastIndexRemoved));
            indicesToRemove[lastIndexRemoved] = i;
            tagsToRemove[lastIndexRemoved] = indexToRemove;
          }
        }

        if (numToAdd > 0) {
          Assertions.assertNotNull(addChildTags != null);
          Assertions.assertNotNull(addAtIndices != null);

          for (lastIndexRemoved = 0; lastIndexRemoved < numToAdd; ++lastIndexRemoved) {
            i = addChildTags.getInt(lastIndexRemoved);
            indexToRemove = addAtIndices.getInt(lastIndexRemoved);
            viewsToAdd[numToMove + lastIndexRemoved] = new ViewAtIndex(i, indexToRemove);
          }
        }

        if (numToRemove > 0) {
          Assertions.assertNotNull(removeFrom != null);

          for (lastIndexRemoved = 0; lastIndexRemoved < numToRemove; ++lastIndexRemoved) {
            i = removeFrom.getInt(lastIndexRemoved);
            indexToRemove = cssNodeToManage.getChildAt(i).getReactTag();
            indicesToRemove[numToMove + lastIndexRemoved] = i;
            tagsToRemove[numToMove + lastIndexRemoved] = indexToRemove;
            tagsToDelete[lastIndexRemoved] = indexToRemove;
          }
        }

        viewsToAdd.sort(ViewAtIndex.comparator);
        indicesToRemove.sort();
        lastIndexRemoved = -1;

        for (i = indicesToRemove.length - 1; i >= 0; --i) {
          indexToRemove = indicesToRemove[i];
          if (indexToRemove == lastIndexRemoved) {
            throw new Error("Repeated indices in Removal list for view tag: " + viewTag);
          }

          cssNodeToManage.removeChildAt(indicesToRemove[i]);
          lastIndexRemoved = indicesToRemove[i];
        }

        for (i = 0; i < viewsToAdd.length; ++i) {
          let viewAtIndex = viewsToAdd[i];
          let cssNodeToAdd = this.mShadowNodeRegistry.getNode(viewAtIndex.mTag);
          if (cssNodeToAdd == null) {
            throw new Error("Trying to add unknown view tag: " + viewAtIndex.mTag);
          }

          cssNodeToManage.addChildAt(cssNodeToAdd, viewAtIndex.mIndex);
        }

        this.mNativeViewHierarchyOptimizer.handleManageChildren(cssNodeToManage, indicesToRemove, tagsToRemove, viewsToAdd, tagsToDelete);

        for (i = 0; i < tagsToDelete.length; ++i) {
          this.removeShadowNode(this.mShadowNodeRegistry.getNode(tagsToDelete[i]));
        }

      } else {
        throw new Error("Size of addChildTags != size of addAtIndices!");
      }
    } else {
      throw new Error("Size of moveFrom != size of moveTo!");
    }
    //}

  }

  public setChildren(viewTag: number, childrenTags: ReadableArray): void {
    if (this.mViewOperationsEnabled) {
      //              synchronized(this.uiImplementationThreadLock) {
      let cssNodeToManage: ReactShadowNode = this.mShadowNodeRegistry.getNode(viewTag);

      for (let i = 0; i < childrenTags.size(); ++i) {
        let cssNodeToAdd = this.mShadowNodeRegistry.getNode(childrenTags.getInt(i));
        if (cssNodeToAdd == null) {
          throw new Error("Trying to add unknown view tag: " + childrenTags.getInt(i));
        }

        cssNodeToManage.addChildAt(cssNodeToAdd, i);
      }

      this.mNativeViewHierarchyOptimizer.handleSetChildren(cssNodeToManage, childrenTags);
      //              }
    }
  }

  //      public replaceExistingNonRootView(int oldTag, int newTag) {
  //          if (!this.mShadowNodeRegistry.isRootNode(oldTag) && !this.mShadowNodeRegistry.isRootNode(newTag)) {
  //              ReactShadowNode oldNode = this.mShadowNodeRegistry.getNode(oldTag);
  //              if (oldNode == null) {
  //                  throw new IllegalViewOperationException("Trying to replace unknown view tag: " + oldTag);
  //              } else {
  //                  ReactShadowNode parent = oldNode.getParent();
  //                  if (parent == null) {
  //                      throw new IllegalViewOperationException("Node is not attached to a parent: " + oldTag);
  //                  } else {
  //                      int oldIndex = parent.indexOf(oldNode);
  //                      if (oldIndex < 0) {
  //                          throw new IllegalStateException("Didn't find child tag in parent");
  //                      } else {
  //                          WritableArray tagsToAdd = Arguments.createArray();
  //                          tagsToAdd.pushInt(newTag);
  //                          WritableArray addAtIndices = Arguments.createArray();
  //                          addAtIndices.pushInt(oldIndex);
  //                          WritableArray indicesToRemove = Arguments.createArray();
  //                          indicesToRemove.pushInt(oldIndex);
  //                          this.manageChildren(parent.getReactTag(), (ReadableArray)null, (ReadableArray)null, tagsToAdd, addAtIndices, indicesToRemove);
  //                      }
  //                  }
  //              }
  //          } else {
  //              throw new IllegalViewOperationException("Trying to add or replace a root tag!");
  //          }
  //      }
  //
  //      public void removeSubviewsFromContainerWithID(int containerTag) {
  //          ReactShadowNode containerNode = this.mShadowNodeRegistry.getNode(containerTag);
  //          if (containerNode == null) {
  //              throw new IllegalViewOperationException("Trying to remove subviews of an unknown view tag: " + containerTag);
  //          } else {
  //              WritableArray indicesToRemove = Arguments.createArray();
  //
  //              for(int childIndex = 0; childIndex < containerNode.getChildCount(); ++childIndex) {
  //                  indicesToRemove.pushInt(childIndex);
  //              }
  //
  //              this.manageChildren(containerTag, (ReadableArray)null, (ReadableArray)null, (ReadableArray)null, (ReadableArray)null, indicesToRemove);
  //          }
  //      }
  //
  //      public void findSubviewIn(int reactTag, float targetX, float targetY, Callback callback) {
  //          this.mOperationsQueue.enqueueFindTargetForTouch(reactTag, targetX, targetY, callback);
  //      }
  //
  //      /** @deprecated */
  //      @Deprecated
  //      public void viewIsDescendantOf(int reactTag, int ancestorReactTag, Callback callback) {
  //          ReactShadowNode node = this.mShadowNodeRegistry.getNode(reactTag);
  //          ReactShadowNode ancestorNode = this.mShadowNodeRegistry.getNode(ancestorReactTag);
  //          if (node != null && ancestorNode != null) {
  //              callback.invoke(new Object[]{node.isDescendantOf(ancestorNode)});
  //          } else {
  //              callback.invoke(new Object[]{false});
  //          }
  //      }

  public measure(reactTag: number, callback: () => void) {
    if (this.mViewOperationsEnabled) {
      this.mOperationsQueue.enqueueMeasure(reactTag, callback);
    }
  }

  public measureInWindow(reactTag: number, callback: () => void) {
    if (this.mViewOperationsEnabled) {
      this.mOperationsQueue.enqueueMeasureInWindow(reactTag, callback);
    }
  }

  public measureLayout(tag: number, ancestorTag: number, errorCallback: () => void, successCallback: () => void) {
    if (this.mViewOperationsEnabled) {
      try {
        this.measureLayoutImpl(tag, ancestorTag, this.mMeasureBuffer);
        let relativeX = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[0]);
        let relativeY = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[1]);
        let width = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[2]);
        let height = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[3]);
        // successCallback.invoke(new Object[]{relativeX, relativeY, width, height});
      } catch (var9) {
        // errorCallback.invoke(new Object[]{var9.getMessage()});
      }

    }
  }

  public measureLayoutRelativeToParent(tag: number, errorCallback: () => void, successCallback: () => void) {
    if (this.mViewOperationsEnabled) {
      try {
        this.measureLayoutRelativeToParentImpl(tag, this.mMeasureBuffer);
        let relativeX = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[0]);
        let relativeY = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[1]);
        let width = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[2]);
        let height = PixelUtil.toDIPFromPixel(this.mMeasureBuffer[3]);
        //successCallback.invoke(new Object[]{relativeX, relativeY, width, height});
      } catch (var8) { // IllegalViewOperationException
        //errorCallback.invoke(new Object[]{var8.getMessage()});
      }

    }
  }

  public onHostPause() {
    //this.mOperationsQueue.pauseFrameCallback();
  }

  public onHostDestroy() {
  }

  public onCatalystInstanceDestroyed() {
    this.mViewOperationsEnabled = false;
  }

  //    public setLayoutAnimationEnabledExperimental(boolean enabled) {
  //        this.mOperationsQueue.enqueueSetLayoutAnimationEnabled(enabled);
  //    }
  //
  //    public configureNextLayoutAnimation(ReadableMap config, Callback success) {
  //        this.mOperationsQueue.enqueueConfigureLayoutAnimation(config, success);
  //    }

  //    public setJSResponder(reactTag: number, blockNativeResponder: boolean): void {
  //        let node = this.mShadowNodeRegistry.getNode(reactTag);
  //        if (node != null) {
  //            while(node.getNativeKind() == NativeKind.NONE) {
  //                node = node.getParent();
  //            }
  //
  //            this.mOperationsQueue.enqueueSetJSResponder(node.getReactTag(), reactTag, blockNativeResponder);
  //        }
  //    }
  //
  //    public clearJSResponder() {
  //        this.mOperationsQueue.enqueueClearJSResponder();
  //    }
  //
  //    /** @deprecated */
  //    @Deprecated
  //    public dispatchViewManagerCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
  //        boolean viewExists = this.checkOrAssertViewExists(reactTag, "dispatchViewManagerCommand: " + commandId);
  //        if (viewExists) {
  //            this.mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs);
  //        }
  //    }
  //
  //    public dispatchViewManagerCommand(int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
  //        boolean viewExists = this.checkOrAssertViewExists(reactTag, "dispatchViewManagerCommand: " + commandId);
  //        if (viewExists) {
  //            this.mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs);
  //        }
  //    }
  //
  //    public showPopupMenu(int reactTag, ReadableArray items, Callback error, Callback success) {
  //        boolean viewExists = this.checkOrAssertViewExists(reactTag, "showPopupMenu");
  //        if (viewExists) {
  //            this.mOperationsQueue.enqueueShowPopupMenu(reactTag, items, error, success);
  //        }
  //    }

  public addUIBlock(block: UIBlock) {
    // this.mOperationsQueue.enqueueUIBlock(block);
  }

  public prependUIBlock(block: UIBlock) {
    //this.mOperationsQueue.prependUIBlock(block);
  }

  public resolveRootTagFromReactTag(reactTag: number): number {
    if (this.mShadowNodeRegistry.isRootNode(reactTag)) {
      return reactTag;
    } else {
      let node: ReactShadowNode = this.resolveShadowNode(reactTag);
      let rootTag: number = 0;
      if (node != null) {
        rootTag = node.getRootTag();
      } else {
        //                FLog.w("ReactNative", "Warning : attempted to resolve a non-existent react shadow node. reactTag=" + reactTag);
      }

      return rootTag;
    }
  }

  public setLayoutUpdateListener(listener: LayoutUpdateListener) {
    this.mLayoutUpdateListener = listener;
  }

  public removeLayoutUpdateListener() {
    this.mLayoutUpdateListener = null;
  }

  protected createRootShadowNode(): ReactShadowNode {
    hilog.info(0x0000, UIImplementation.TAG, 'createRootShadowNode')

    let rootCSSNode: ReactShadowNode = new ReactShadowNodeImpl();
    //        I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
    //        if (sharedI18nUtilInstance.isRTL(this.mReactContext)) {
    //            rootCSSNode.setLayoutDirection(YogaDirection.RTL);
    //        }

    rootCSSNode.setViewTypeName("Root");
    return rootCSSNode;
  }

  //    public setViewHierarchyUpdateDebugListener(listener: NotThreadSafeViewHierarchyUpdateDebugListener) {
  ////        this.mOperationsQueue.setViewHierarchyUpdateDebugListener(listener);
  //    }

  protected createShadowNode(className: string): ReactShadowNode {
    hilog.info(0x0000, UIImplementation.TAG, 'createShadowNode')
    let viewManager: ReactShadowNodeViewManager = this.mViewManagers.get(className);
    return viewManager?.createShadowNodeInstance(this.mReactContext);
  }

  //    @Nullable
  protected resolveViewManager(className: string): ReactShadowNodeViewManager {
    hilog.info(0x0000, UIImplementation.TAG, 'resolveViewManager')
    return this.mViewManagers.getViewManagerIfExists(className);
  }

  protected handleCreateView(cssNode: ReactShadowNode, rootViewTag: number, styles?: ReactStylesDiffMap): void {
    hilog.info(0x0000, UIImplementation.TAG, 'handleCreateView %{public}d', 111)
    // if (!cssNode.isVirtual()) {
    this.mNativeViewHierarchyOptimizer.handleCreateView(cssNode, cssNode?.getThemedContext(), styles);
    //}
  }

  //
  protected updateViewHierarchy(): void {
    //    Systrace.beginSection(0L, "UIImplementation.updateViewHierarchy");

    try {
      for (let i = 0; i < this.mShadowNodeRegistry.getRootNodeCount(); ++i) {
        let tag = this.mShadowNodeRegistry.getRootTag(i);
        let cssRoot = this.mShadowNodeRegistry.getNode(tag);
        if (cssRoot.getWidthMeasureSpec() != null && cssRoot.getHeightMeasureSpec() != null) {
          //                SystraceMessage.beginSection(0L, "UIImplementation.notifyOnBeforeLayoutRecursive").arg("rootTag", cssRoot.getReactTag()).flush();

          try {
            this.notifyOnBeforeLayoutRecursive(cssRoot);
          } finally {
            //                    Systrace.endSection(0L);
          }

          this.calculateRootLayout(cssRoot);
          //                SystraceMessage.beginSection(0L, "UIImplementation.applyUpdatesRecursive").arg("rootTag", cssRoot.getReactTag()).flush();

          try {
            this.applyUpdatesRecursive(cssRoot, 0.0, 0.0);
          } finally {
            //                    Systrace.endSection(0L);
          }

          if (this.mLayoutUpdateListener != null) {
            this.mOperationsQueue.enqueueLayoutUpdateFinished(cssRoot, this.mLayoutUpdateListener);
          }
        }
      }
    } finally {
      //        Systrace.endSection(0L);
    }

  }

  protected removeShadowNode(nodeToRemove: ReactShadowNode) {
    this.removeShadowNodeRecursive(nodeToRemove);
    nodeToRemove.dispose();
  }

  protected calculateRootLayout(cssRoot: ReactShadowNode): void {
    // SystraceMessage.beginSection(0L, "cssRoot.calculateLayout").arg("rootTag", cssRoot.getReactTag()).flush();
    // long startTime = SystemClock.uptimeMillis();

    try {
      let widthSpec = cssRoot.getWidthMeasureSpec();
      let heightSpec = cssRoot.getHeightMeasureSpec();
      // cssRoot.calculateLayout(MeasureSpec.getMode(widthSpec) == 0 ? 0.0F / 0.0 : MeasureSpec.getSize(widthSpec), MeasureSpec.getMode(heightSpec) == 0 ? 0.0F / 0.0 : MeasureSpec.getSize(heightSpec));
    } finally {
      //              Systrace.endSection(0L);
      //              this.mLastCalculateLayoutTime = SystemClock.uptimeMillis() - startTime;
    }

  }

  protected applyUpdatesRecursive(cssNode: ReactShadowNode, absoluteX: number, absoluteY: number) {
    if (cssNode.hasUpdates()) {
      // Iterable<? extends ReactShadowNode>
      let iter = cssNode.calculateLayoutOnChildren();
      if (iter != null) {
        let cssChild = iter.next().value as ReactShadowNode;
        while (cssChild != undefined) {
          this.applyUpdatesRecursive(cssChild, absoluteX + cssNode.getLayoutX(), absoluteY + cssNode.getLayoutY());
          cssChild = iter.next().value as ReactShadowNode;
        }
      }

      let tag = cssNode.getReactTag();
      if (!this.mShadowNodeRegistry.isRootNode(tag)) {
        let frameDidChange = cssNode.dispatchUpdates(absoluteX, absoluteY, this.mOperationsQueue, this.mNativeViewHierarchyOptimizer);
        if (frameDidChange && cssNode.shouldNotifyOnLayout()) {
          //this.mEventDispatcher.dispatchEvent(OnLayoutEvent.obtain(-1, tag, cssNode.getScreenX(), cssNode.getScreenY(), cssNode.getScreenWidth(), cssNode.getScreenHeight()));
        }
      }
      cssNode.markUpdateSeen();
      this.mNativeViewHierarchyOptimizer.onViewUpdatesCompleted(cssNode);
    }
  }

  private measureLayoutImpl(tag: number, ancestorTag: number, outputBuffer: number[]): void {
    let node = this.mShadowNodeRegistry.getNode(tag);
    let ancestor = this.mShadowNodeRegistry.getNode(ancestorTag);
    if (node != null && ancestor != null) {
      if (node != ancestor) {
        for (let currentParent = node.getParent(); currentParent != ancestor; currentParent = currentParent.getParent()) {
          if (currentParent == null) {
            throw new Error("Tag " + ancestorTag + " is not an ancestor of tag " + tag);
          }
        }
      }

      this.measureLayoutRelativeToVerifiedAncestor(node, ancestor, outputBuffer);
    } else {
      throw new Error("Tag " + (node == null ? tag : ancestorTag) + " does not exist");
    }
  }

  private measureLayoutRelativeToParentImpl(tag: number, outputBuffer: number[]): void {
    let node = this.mShadowNodeRegistry.getNode(tag);
    if (node == null) {
      throw new Error("No native view for tag " + tag + " exists!");
    } else {
      let parent = node.getParent();
      if (parent == null) {
        throw new Error("View with tag " + tag + " doesn't have a parent!");
      } else {
        this.measureLayoutRelativeToVerifiedAncestor(node, parent, outputBuffer);
      }
    }
  }

  private measureLayoutRelativeToVerifiedAncestor(node: ReactShadowNode, ancestor: ReactShadowNode, outputBuffer: number[]): void {
    let offsetX = 0;
    let offsetY = 0;
    if (node != ancestor && !node.isVirtual()) {
      offsetX = Math.round(node.getLayoutX());
      offsetY = Math.round(node.getLayoutY());

      for (let current = node.getParent(); current != ancestor; current = current.getParent()) {
        Assertions.assertNotNull(current != null);
        this.assertNodeDoesNotNeedCustomLayoutForChildren(current);
        offsetX += Math.round(current.getLayoutX());
        offsetY += Math.round(current.getLayoutY());
      }

      this.assertNodeDoesNotNeedCustomLayoutForChildren(ancestor);
    }

    outputBuffer[0] = offsetX;
    outputBuffer[1] = offsetY;
    outputBuffer[2] = node.getScreenWidth();
    outputBuffer[3] = node.getScreenHeight();
  }

  private assertNodeDoesNotNeedCustomLayoutForChildren(node: ReactShadowNode): void {
    //          let viewManager = (ViewManager)Assertions.assertNotNull(this.mViewManagers.get(node.getViewClass()));
    //          if (viewManager instanceof IViewManagerWithChildren) {
    //              IViewManagerWithChildren viewManagerWithChildren = (IViewManagerWithChildren)viewManager;
    //              if (viewManagerWithChildren != null && viewManagerWithChildren.needsCustomLayoutForChildren()) {
    //                  throw new IllegalViewOperationException("Trying to measure a view using measureLayout/measureLayoutRelativeToParent relative to an ancestor that requires custom layout for it's children (" + node.getViewClass() + "). Use measure instead.");
    //              }
    //          } else {
    //              throw new IllegalViewOperationException("Trying to use view " + node.getViewClass() + " as a parent, but its Manager doesn't extends ViewGroupManager");
    //          }
  }

  private notifyOnBeforeLayoutRecursive(cssNode: ReactShadowNode): void {
    if (cssNode.hasUpdates()) {
      for (let i = 0; i < cssNode.getChildCount(); ++i) {
        this.notifyOnBeforeLayoutRecursive(cssNode.getChildAt(i));
      }

      cssNode.onBeforeLayout(this.mNativeViewHierarchyOptimizer);
    }
  }

  private dispatchViewUpdatesIfNeeded() {
    if (this.mOperationsQueue.isEmpty()) {
      this.dispatchViewUpdates(-1);
    }

  }

  private removeShadowNodeRecursive(nodeToRemove: ReactShadowNode) {
    NativeViewHierarchyOptimizer.handleRemoveNode(nodeToRemove);
    this.mShadowNodeRegistry.removeNode(nodeToRemove.getReactTag());

    // for(i = nodeToRemove.getChildCount() - 1; i >= 0; --i) {
    //   this.removeShadowNodeRecursive(nodeToRemove.getChildAt(i));
    // }

    nodeToRemove.removeAndDisposeAllChildren();
  }

  private checkOrAssertViewExists(reactTag: number, operationNameForExceptionMessage: string): boolean {
    let viewExists: boolean = this.mShadowNodeRegistry.getNode(reactTag) != null;
    if (viewExists) {
      return true;
    } else {
      let message = "Unable to execute operation " + operationNameForExceptionMessage + " on view with tag: " + reactTag + ", since the view does not exist";
      if (ReactBuildConfig.DEBUG) {
        throw new Error(message);
      } else {
        hilog.debug(0x0000, "ReactNative", message);
        return false;
      }
    }
  }
}
